package aceim.api.service;
import java.util.ArrayList;
import java.util.List;
import aceim.api.dataentity.Buddy;
import aceim.api.dataentity.BuddyGroup;
import aceim.api.dataentity.ItemAction;
import aceim.api.dataentity.Message;
import aceim.api.dataentity.OnlineInfo;
import aceim.api.dataentity.ProtocolOption;
import aceim.api.dataentity.ProtocolServiceFeature;
import aceim.api.utils.Logger;
import aceim.api.utils.Logger.LoggerLevel;
import aceim.api.utils.ServiceHelper;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
/**
* Protocol service base, contains all protocol and account management logic. Should be nested by protocol implementation.
* @param <T>
*/
public abstract class ProtocolService<T extends AccountService> extends Service {
/**
* Service helper utility entity.
*/
private final ServiceHelper mHelper = new ServiceHelper(this);
/**
* List of running accounts.
*/
protected final List<T> mAccountServices = new ArrayList<T>();
private ICoreProtocolCallback callback = null;
/**
* AIDL core-to-protocol interface stub.
*/
private final IProtocolService.Stub mainService = new IProtocolService.Stub() {
@Override
public void uploadAccountPhoto(byte serviceId, String filePath) throws RemoteException {
T service = findAccountServiceById(serviceId);
service.getProtocol().uploadAccountPhoto(filePath);
}
@Override
public void shutdown() throws RemoteException {
shutdownInternal();
}
@Override
public void setFeature(String featureId, OnlineInfo info) throws RemoteException {
T service = findAccountServiceById(info.getServiceId());
service.getProtocol().setFeature(featureId, info);
}
@Override
public void sendTypingNotification(byte serviceId, String ownerUid) throws RemoteException {
T service = findAccountServiceById(serviceId);
service.getProtocol().sendTypingNotification(ownerUid);
}
@Override
public long sendMessage(Message message) throws RemoteException {
T service = findAccountServiceById(message.getServiceId());
return service.getProtocol().sendMessage(message);
}
@Override
public void requestIcon(byte serviceId, String ownerUid) throws RemoteException {
T service = findAccountServiceById(serviceId);
service.getProtocol().requestIcon(ownerUid);
}
@Override
public void requestFullInfo(byte serviceId, String uid, boolean shortInfo) throws RemoteException {
T service = findAccountServiceById(serviceId);
service.getProtocol().requestFullInfo(uid, shortInfo);
}
@Override
public void removeAccountPhoto(byte serviceId) throws RemoteException {
T service = findAccountServiceById(serviceId);
service.getProtocol().removeAccountPhoto();
}
@Override
public void removeAccount(byte serviceId) throws RemoteException {
T service = findAccountServiceById(serviceId);
service.getProtocol().disconnect();
removeAccountService(serviceId);
}
@Override
public void registerCallback(ICoreProtocolCallback callback) throws RemoteException {
ProtocolService.this.callback = callback;
}
@Override
public void messageResponse(Message message, boolean accept) throws RemoteException {
T service = findAccountServiceById(message.getServiceId());
service.getProtocol().messageResponse(message, accept);
}
@Override
public void leaveChatRoom(byte serviceId, String chatId) throws RemoteException {
T service = findAccountServiceById(serviceId);
service.getProtocol().leaveChatRoom(chatId);
}
@Override
public void joinChatRoom(byte serviceId, String chatId, boolean loadOccupantsIcons) throws RemoteException {
T service = findAccountServiceById(serviceId);
service.getProtocol().joinChatRoom(chatId, loadOccupantsIcons);
}
@Override
public ProtocolOption[] getProtocolOptions() throws RemoteException {
return ProtocolService.this.getProtocolOptions();
}
@Override
public String getProtocolName() throws RemoteException {
return ProtocolService.this.getProtocolName();
}
@Override
public ProtocolServiceFeature[] getProtocolFeatures() throws RemoteException {
return ProtocolService.this.getProtocolFeatures();
}
@Override
public void disconnect(byte serviceId) throws RemoteException {
T service = findAccountServiceById(serviceId);
service.getProtocol().disconnect();
}
@Override
public void connect(OnlineInfo info) throws RemoteException {
T service = findAccountServiceById(info.getServiceId());
service.getProtocol().connect(info);
}
@Override
public void cancelFileFransfer(byte serviceId, long messageId) throws RemoteException {
T service = findAccountServiceById(serviceId);
service.getProtocol().cancelFileFransfer(messageId);
}
@Override
public void buddyGroupAction(ItemAction action, BuddyGroup group) throws RemoteException {
T service = findAccountServiceById(group.getServiceId());
service.getProtocol().buddyGroupAction(action, group);
}
@Override
public void buddyAction(ItemAction action, Buddy buddy) throws RemoteException {
T service = findAccountServiceById(buddy.getServiceId());
service.getProtocol().buddyAction(action, buddy);
}
@Override
public void addAccount(byte serviceId, String protocolUid) throws RemoteException {
addAccountService(serviceId, protocolUid);
}
@Override
public void logToFile(boolean enable) throws RemoteException {
Logger.logToFile = enable;
}
};
@Override
public IBinder onBind(Intent intent) {
Logger.log(getPackageName() + ": CoreService binded", LoggerLevel.VERBOSE);
disconnectAll();
mAccountServices.clear();
return mainService;
}
@Override
public boolean onUnbind(Intent intent) {
Logger.log(getPackageName() + ": CoreService unbinded", LoggerLevel.VERBOSE);
shutdownInternal();
return false;
}
public ICoreProtocolCallback getCallback() {
return callback;
}
/**
* Create account entity by service ID and protocol UID.
*
* @param serviceId
* @param protocolUid
*/
private void addAccountService(byte serviceId, String protocolUid) {
for (T service : mAccountServices) {
if (service.getProtocolUid().equals(protocolUid)) {
Logger.log(getPackageName() + ": Existing account service found for id#" + protocolUid, LoggerLevel.VERBOSE);
service.getProtocol().disconnect();
mAccountServices.remove(service);
break;
}
}
Logger.log(getPackageName() + ": Adding account service " + protocolUid, LoggerLevel.VERBOSE);
if (mAccountServices.size() < 1) {
mHelper.doStartForeground();
}
mAccountServices.add(createService(serviceId, protocolUid));
}
/**
* Remove account entity by service ID.
*
* @param serviceId
*/
private void removeAccountService(byte serviceId) {
Logger.log(getPackageName() + ": Removing account service " + serviceId, LoggerLevel.VERBOSE);
mAccountServices.remove(findAccountServiceById(serviceId));
if (mAccountServices.size() < 1) {
mHelper.doStopForeground();
}
}
private void shutdownInternal() {
Logger.log(getPackageName() + ": Shutdown request", LoggerLevel.VERBOSE);
disconnectAll();
mHelper.doStopForeground();
//stopSelfResult(startId);
stopSelf();
}
private void disconnectAll() {
Logger.log(getPackageName() + ": Disconnect all", LoggerLevel.VERBOSE);
for (T service : mAccountServices) {
try {
mainService.disconnect(service.getServiceId());
} catch (RemoteException e) {
Logger.log(e);
}
}
}
protected T findAccountServiceById(byte serviceId) {
for (T s : mAccountServices) {
if (s.getServiceId() == serviceId) {
return s;
}
}
return null;
}
@Override
public void onDestroy() {
super.onDestroy();
Logger.log(getPackageName() + ": Destroyed", LoggerLevel.VERBOSE);
}
/**
* Get {@link ProtocolOption} set for current protocol, for account editor form.
*
* @return options list, very likely to be ordered.
*/
protected abstract ProtocolOption[] getProtocolOptions();
/**
* Get {@link ProtocolServiceFeature} set for current protocol.
*
* @return features list, may not preserve order.
*/
protected abstract ProtocolServiceFeature[] getProtocolFeatures();
/**
* Create account implementation, by service ID & protocol UID.
*
* @param serviceId
* @param protocolUid
* @return
*/
protected abstract T createService(byte serviceId, String protocolUid) ;
/**
* Get implemented protocol name (XMPP, ICQ etc)
* @return
*/
protected abstract String getProtocolName();
}